package com.zhiyuan.config;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.zhiyuan.manager.TwoLevelCacheManager;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;

import java.nio.charset.StandardCharsets;
import java.time.Duration;

/**
 *
 *
 * @author zhiyuan
 * @date 2025/10/25
 */
@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.afterPropertiesSet();

        // L2: Redis Cache
        RedisCacheManager redisMgr = RedisCacheManager.builder(factory)
                .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()
                        .entryTtl(Duration.ofMinutes(5)))
                .build();

        // L1: Caffeine Cache
        CaffeineCacheManager caffeineMgr = new CaffeineCacheManager();
        caffeineMgr.setCaffeine(Caffeine.newBuilder()
                .maximumSize(10_000)
                .expireAfterWrite(Duration.ofMinutes(5)));

        // 组合：先查 L1，再查 L2；put 时写两边
        return new TwoLevelCacheManager(caffeineMgr, redisMgr);
    }

    // 订阅失效广播，清 L1
    @Bean
    public MessageListenerAdapter invalidateListener(TwoLevelCacheManager mgr) {
        return new MessageListenerAdapter((MessageListener) (msg, pattern) -> {
            String key = new String(msg.getBody(), StandardCharsets.UTF_8);
            mgr.invalidateLocal(key);
        });
    }

    @Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory cf,
                                                   MessageListenerAdapter listener) {
        RedisMessageListenerContainer c = new RedisMessageListenerContainer();
        c.setConnectionFactory(cf);
        c.addMessageListener(listener, new PatternTopic("cache:invalidate"));
        return c;
    }

    // 配置片段（与之前你用的类似）
    @Bean
    public TwoLevelCacheManager twoLevelCacheManager(CaffeineCacheManager caffeineMgr,
                                                     RedisCacheManager redisMgr,
                                                     StringRedisTemplate stringRedisTemplate) {
        return new TwoLevelCacheManager(caffeineMgr, redisMgr, stringRedisTemplate, "cache:invalidate");
    }

    @Bean
    public RedisMessageListenerContainer cacheInvalidateListenerContainer(
            RedisConnectionFactory cf,
            TwoLevelCacheManager twoLevelCacheManager) {
        RedisMessageListenerContainer c = new RedisMessageListenerContainer();
        c.setConnectionFactory(cf);
        c.addMessageListener((message, pattern) -> {
            String payload = new String(message.getBody(), java.nio.charset.StandardCharsets.UTF_8);
            // 解析：cacheName::key 或 cacheName::*
            int idx = payload.indexOf("::");
            if (idx > 0) {
                String cacheName = payload.substring(0, idx);
                String key = payload.substring(idx + 2);
                if ("*".equals(key)) {
                    twoLevelCacheManager.invalidateLocal(cacheName);
                } else {
                    twoLevelCacheManager.invalidateLocal(cacheName, key);
                }
            }
        }, new org.springframework.data.redis.listener.ChannelTopic("cache:invalidate"));
        return c;
    }

}
